/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.openide.text; import java.lang.ref.WeakReference; import java.awt.Color; import java.awt.Component; import java.beans.*; import javax.swing.text.*; import javax.swing.JEditorPane; import org.openide.options.SystemOption; import org.openide.util.HelpCtx; import org.openide.util.NbBundle; /** Dummy class holding utility methods for working with NetBeans document conventions. * * @author Jaroslav Tulach */ public final class NbDocument extends Object { private NbDocument() {} /** Attribute that signals that a given character is guarded (cannot * be modified). Implements {@link AttributeSet.CharacterAttribute} to signal that * this attribute applies to characters, not paragraphs. */ public static final Object GUARDED = new AttributeSet.CharacterAttribute () {}; /** Attribute set that adds to a part of document guarded flag */ private static final SimpleAttributeSet ATTR_ADD = new SimpleAttributeSet (); /** Attribute set to remove the guarded flag. */ private static final SimpleAttributeSet ATTR_REMOVE = new SimpleAttributeSet (); static { ATTR_ADD.addAttribute(GUARDED, Boolean.TRUE); ATTR_REMOVE.addAttribute(GUARDED, Boolean.FALSE); } /** Common colors of breakpoint lines, erroneous lines, etc. */ public static final Colors COLORS = new Colors (); /** Name of style attached to documents to mark a paragraph (line) * as a (debugger) breakpoint. */ public static final String BREAKPOINT_STYLE_NAME = "NbBreakpointStyle"; // NOI18N /** Name of style attached to documents to mark a paragraph (line) * as erroneous. */ public static final String ERROR_STYLE_NAME = "NbErrorStyle"; // NOI18N /** Name of style attached to documents to mark a paragraph (line) * as current (in a debugger). */ public static final String CURRENT_STYLE_NAME = "NbCurrentStyle"; // NOI18N /** Name of style attached to documents to unmark a paragraph (line) * as anything special. */ public static final String NORMAL_STYLE_NAME = "NbNormalStyle"; // NOI18N /** Find the root element of all lines. * All conforming NetBeans documents * should return a valid element. * * @param doc styled document (expecting NetBeans document) * @return the root element */ public static Element findLineRootElement (StyledDocument doc) { Element e = doc.getParagraphElement (0).getParentElement (); if (e == null) { // try default root (should work for text/plain) e = doc.getDefaultRootElement (); } return e; } /** For given document and an offset, find the line number. * @param doc the document * @param offset offset in the document * @return the line number for that offset */ public static int findLineNumber (StyledDocument doc, int offset) { Element paragraphsParent = findLineRootElement (doc); return paragraphsParent.getElementIndex (offset); } /** Finds column number given an offset. * @param doc the document * @param offset offset in the document * @return column within the line of that offset (counting starts from zero) */ public static int findLineColumn (StyledDocument doc, int offset) { Element paragraphsParent = findLineRootElement (doc); int indx = paragraphsParent.getElementIndex (offset); return offset - paragraphsParent.getElement (indx).getStartOffset (); } /** Finds offset of the beginning of a line. * @param doc the document * @param line number of the line to find the start of * @return offset */ public static int findLineOffset (StyledDocument doc, int lineNumber) { Element paragraphsParent = findLineRootElement (doc); return paragraphsParent.getElement (lineNumber).getStartOffset (); } /** Creates position with a bias. If the bias is {@link Position.Bias#Backward} * then if an insert occures at the position, the text is inserted * after the position. If the bias is {@link Position.Bias#Forward <code>Forward</code>}, then the text is * inserted before the position. * <P> * The method checks if the document implements {@link PositionBiasable}, * and if so, {@link PositionBiasable#createPosition <code>createPosition</code>} is called. * Otherwise an attempt is made to provide a <code>Position</code> with the correct behavior. * * @param doc document to create position in * @param offset the current offset for the position * @param bias the bias to use for the position * @exception BadLocationException if the offset is invalid */ public static Position createPosition ( Document doc, int offset, Position.Bias bias ) throws BadLocationException { if (doc instanceof PositionBiasable) { return ((PositionBiasable)doc).createPosition (offset, bias); } else { if (bias == Position.Bias.Forward) { // default behaviour return doc.createPosition (offset); } else { // use our special position return BackwardPosition.create (doc, offset); } } } /** Mark part of a document as guarded (immutable to the user). * @param doc styled document * @param offset offset to start at * @param len length of text to mark as guarded */ public static void markGuarded (StyledDocument doc, int offset, int len) { doc.setCharacterAttributes (offset, len, ATTR_ADD, false); } /** Remove guarded mark on a block of a document. * @param doc styled document * @param offset offset to start at * @param len length of text to mark as unguarded */ public static void unmarkGuarded (StyledDocument doc, int offset, int len) { doc.setCharacterAttributes (offset, len, ATTR_REMOVE, false); } /** Inserts a text into given offset and marks it guarded. * @param doc document to insert to * @param offset offset of insertion * @param txt string text to insert */ public static void insertGuarded (StyledDocument doc, int offset, String txt) throws BadLocationException { doc.insertString (offset, txt, ATTR_ADD); } /** Attach a breakpoint to a line in the document. * If the document has a defined style named {@link #BREAKPOINT_STYLE_NAME}, it is used. * Otherwise, a new style is defined. * * @param doc the document * @param offset identifies the line to set breakpoint to */ public static void markBreakpoint (StyledDocument doc, int offset) { Style bp = doc.getStyle (BREAKPOINT_STYLE_NAME); if (bp == null) { // create the style bp = doc.addStyle (BREAKPOINT_STYLE_NAME, null); if (bp == null) return; ColorListener.createListener (bp, COLORS.getBreakpoint (), Color.white, BREAKPOINT_STYLE_NAME); } doc.setLogicalStyle (offset, bp); } /** Mark a line as erroneous (e.g. by the compiler). * If the document has a defined style named {@link #ERROR_STYLE_NAME}, it is used. * Otherwise, a new style is defined. * * @param doc the document * @param offset identifies the line to mark */ public static void markError (StyledDocument doc, int offset) { Style bp = doc.getStyle (ERROR_STYLE_NAME); if (bp == null) { // create the style bp = doc.addStyle (ERROR_STYLE_NAME, null); if (bp == null) return; ColorListener.createListener (bp, COLORS.getError (), Color.white, ERROR_STYLE_NAME); } doc.setLogicalStyle (offset, bp); } /** Marks a line as current (e.g. for the debugger). * If the document has a defined style named {@link #CURRENT_STYLE_NAME}, it is used. * Otherwise, a new style is defined. * * @param doc the document * @param offset identifies the line to mark */ public static void markCurrent (StyledDocument doc, int offset) { Style bp = doc.getStyle (CURRENT_STYLE_NAME); if (bp == null) { // create the style bp = doc.addStyle (CURRENT_STYLE_NAME, null); if (bp == null) return; ColorListener.createListener (bp, COLORS.getCurrent (), Color.white, CURRENT_STYLE_NAME); } doc.setLogicalStyle (offset, bp); } /** * Mark a line as normal (no special attributes). * This uses the dummy style named {@link #NORMAL_STYLE_NAME}. * This method should be used to undo the effect of {@link #markBreakpoint}, {@link #markError} and {@link #markCurrent}. * @param doc the document * @param offset identified the line to unmark */ public static void markNormal (StyledDocument doc, int offset) { Style st = doc.getStyle (NORMAL_STYLE_NAME); if (st == null) st = doc.addStyle (NORMAL_STYLE_NAME, null); if (st != null) { doc.setLogicalStyle (offset, st); } } /** Locks the document to have exclusive access to it. * Documents implementing {@link Lockable} can specify exactly how to do this. * * @param doc document to lock * @param run the action to run */ public static void runAtomic (StyledDocument doc, Runnable run) { if (doc instanceof WriteLockable) { // use the method ((WriteLockable)doc).runAtomic (run); } else { // transfer the runnable to event dispatch thread synchronized (doc) { run.run (); } } } /** Executes given runnable in "user mode" does not allowing any modifications * to parts of text marked as guarded. The actions should be run as "atomic" so * either happen all at once or none at all (if a guarded block should be modified). * * @param doc document to modify * @param run runnable to run in user mode that will have exclusive access to modify the document * @exception BadLocationException if a modification of guarded text occured * and that is why no changes to the document has been done. */ public static void runAtomicAsUser (StyledDocument doc, Runnable run) throws BadLocationException { if (doc instanceof WriteLockable) { // use the method ((WriteLockable)doc).runAtomicAsUser (run); } else { // transfer the runnable to event dispatch thread synchronized (doc) { run.run (); } } } /** Find a way to print a given document. * If the document implements the correct interface(s) then the document is returned, * else {@link DefaultPrintable} is used as a wrapper. In this last case it is useful * to implement {@link NbDocument.Printable} to describe how to print in terms of * attributed characters, rather than specifying the complete page layout from scratch. * * @param doc the document to find printing support for * @return an object that is instance of eith {@link java.awt.print.Printable} or {@link java.awt.print.Pageable} */ public static Object findPageable(StyledDocument doc) { if (doc instanceof java.awt.print.Pageable) { return doc; } else if (doc instanceof java.awt.print.Printable) { return doc; } else { return new DefaultPrintable(doc); } } /** Class that holds all colors used in the editor for OpenIDE-specific purposes. * It includes colors for breakpoints, current lines, etc. */ public static final class Colors extends SystemOption { /** Property name for breakpoint color. */ public static final String PROP_BREAKPOINT = BREAKPOINT_STYLE_NAME; /** Property name for erroneous line color. */ public static final String PROP_ERROR = ERROR_STYLE_NAME; /** Property name for current line color. */ public static final String PROP_CURRENT = CURRENT_STYLE_NAME; /** Color for breakpoints. */ private static Color breakpoint = new Color (127, 127, 255); /** error line */ private static Color error = Color.red; /** current line color */ private static Color current = Color.magenta; /* @return display name */ static final long serialVersionUID =-9152250591365746193L; public String displayName () { return NbBundle.getBundle (NbDocument.class).getString ("MSG_COLORS"); } public HelpCtx getHelpCtx () { return new HelpCtx (Colors.class); } /** Set breakpoint color. This changes color in all editors that * react to {@link #BREAKPOINT_STYLE_NAME} style. * @param c new color */ public void setBreakpoint (Color c) { Color old = breakpoint; breakpoint = c; firePropertyChange (PROP_BREAKPOINT, old, c); } /** Get breakpoint color. * @return the color */ public Color getBreakpoint () { return breakpoint; } /** Set erroneous line color. This changes color in all editors that * reacts to {@link #ERROR_STYLE_NAME} style. * @param c new color */ public void setError (Color c) { Color old = error; error = c; firePropertyChange (PROP_ERROR, old, c); } /** Get color of erroneous lines. * @return the color */ public Color getError () { return error; } /** Set current line color. This changes color in all editors that * reacts to {@link #CURRENT_STYLE_NAME} style. * @param c new color */ public void setCurrent (Color c) { Color old = current; current = c; firePropertyChange (PROP_CURRENT, old, c); } /** Get current line color. * @return the color */ public Color getCurrent () { return current; } } /** Listener on change of color. When it happens the * associated style's background attribute is modified. */ private static final class ColorListener implements PropertyChangeListener { /** Style to watch over */ private WeakReference style; /** property to watch for */ private String propName; /** * @param s the style * @param c the color to assign * @param n property to watch changes of */ public static void createListener (Style s, Color backgroundColor, Color foregroundColor, String n) { s.addAttribute ( StyleConstants.ColorConstants.Background, backgroundColor ); s.addAttribute ( StyleConstants.ColorConstants.Foreground, foregroundColor ); COLORS.addPropertyChangeListener (new ColorListener (s, n)); } /** * @param style the style to set the color to * @param color current color */ private ColorListener (Style style, String n) { this.style = new WeakReference (style); propName = n; } /** Changes the color in the style. */ public void propertyChange (PropertyChangeEvent ev) { if (propName.equals (ev.getPropertyName ())) { Style s = (Style)style.get (); if (s == null) { // deregister COLORS.removePropertyChangeListener (this); return; } s.addAttribute ( StyleConstants.ColorConstants.Background, (Color)ev.getNewValue () ); } } } /** Specialized version of document that knows how to lock the document * for complex modifications. */ public interface WriteLockable extends Document { /** Executes given runnable in lock mode of the document. * In this mode, all redrawing is stopped and the caller has exclusive * access to all modifications to the document. * <P> * By definition there should be only one locker at a time. Sample implementation, if you are extending {@link AbstractDocument}: * * <p><code> * writeLock();<br> * try {<br> *   r.run();<br> * } finally {<br> *   writeUnlock();<br> * } * </code> * * @param run runnable to run while locked * * @see NbDocument#runAtomic */ public void runAtomic (Runnable r); /** Executes given runnable in "user mode" does not allowing any modifications * to parts of text marked as guarded. The actions should be run as "atomic" so * either happen all at once or none at all (if a guarded block should be modified). * * @param run runnable to run in user mode that will have exclusive access to modify the document * @exception BadLocationException if a modification of guarded text occured * and that is why no changes to the document has been done. */ public void runAtomicAsUser (Runnable r) throws BadLocationException; } /** Document which may support styled text printing. * Any document that wishes to support special formatting while printing * can implement this interface and provide a <code>AttributedCharacterIterator</code> * specifying colors, fonts, etc. */ public interface Printable extends Document { /** Get an attributed character iterator for the document, so that it may be printed. * <p>For a convenient way to do this, you may use {@link AttributedCharacters#iterator a simple implementation} * of an * attributed character list. * @return list of <code>AttributedCharacterIterator</code>s to be used for printing * * @see NbDocument#findPageable */ public java.text.AttributedCharacterIterator[] createPrintIterators(); } /** Enhanced version of document that provides better support for * holding and working with biased positions. It adds one new method * {@link #createPosition} that creates * a position that moves either to the left or to the right when an insertion * is performed at it. * <P> * If a document implements this interface, the new method is * used in {@link NbDocument#createPosition}. * If not, special support for the position is created. */ public interface PositionBiasable extends Document { /** Creates position with a bias. If the bias is {@link Position.Bias#Backward} * then if an insert occures at the position, the text is inserted * after the position. If the bias is {@link Position.Bias#Forward <code>Forward</code>}, then the text is * inserted before the position. * * @param offset the offset for the position * @param bias the bias to use for the position * @exception BadLocationException if the offset is invalid * * @see NbDocument#createPosition */ public Position createPosition (int offset, Position.Bias bias) throws BadLocationException; } /** Enabled documents to add special UI components to their Editor pane. * If this interface is implemented by the Editor document, it can be used * to add other components (such as toolbars) to the pane. */ public interface CustomEditor extends Document { /** Create a whole editor component over the given <code>JEditorPane</code>. * The implementation should generally add some kind of scrolling * support to the given <code>JEditorPane</code> (typically with scrollbars), * possibly some other components * according to the desired layout, * and return the resulting container. * @param j editor pane over which the resulting component * will be built * @return component encapsulating the pane and all other * custom UI components */ public Component createEditor(JEditorPane j); } } /* * Log * 35 src-jtulach1.34 1/13/00 Ian Formanek NOI18N * 34 src-jtulach1.33 10/22/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 33 src-jtulach1.32 10/7/99 Miloslav Metelka foreground color in * styles * 32 src-jtulach1.31 9/25/99 Jaroslav Tulach runAtomic on document not * supporting it directly does not use invokeAndWait but simply synchronize * on the document. * 31 src-jtulach1.30 8/17/99 Ian Formanek Generated serial version * UID * 30 src-jtulach1.29 7/27/99 Miloslav Metelka Colors updated * 29 src-jtulach1.28 7/2/99 Jesse Glick Help IDs for system * options. * 28 src-jtulach1.27 6/9/99 Ian Formanek manifest tags changed to * NetBeans- * 27 src-jtulach1.26 6/8/99 Ian Formanek ---- Package Change To * org.openide ---- * 26 src-jtulach1.25 6/4/99 Ales Novak # 1970 * 25 src-jtulach1.24 5/11/99 Ales Novak new constant adde + only * constructor for DefaultPrintable used * 24 src-jtulach1.23 4/21/99 Jesse Glick [JavaDoc] * 23 src-jtulach1.22 4/21/99 Miloslav Metelka Added CustomEditor * handling * 22 src-jtulach1.21 4/21/99 Ales Novak changes to printing * 21 src-jtulach1.20 4/9/99 David Simonek bugfix #1429 * 20 src-jtulach1.19 3/26/99 Ian Formanek Fixed use of obsoleted * NbBundle.getBundle (this) * 19 src-jtulach1.18 3/19/99 Petr Hamernik bugfix * 18 src-jtulach1.17 3/17/99 Jaroslav Tulach Output Window fixing. * 17 src-jtulach1.16 3/15/99 Jesse Glick Utility classes ought not * have public constructors. * 16 src-jtulach1.15 2/26/99 Jesse Glick [JavaDoc] * 15 src-jtulach1.14 2/26/99 Jesse Glick Added markNormal() to * undo effects of others. invokeSafe() is private. * 14 src-jtulach1.13 2/24/99 Jesse Glick [JavaDoc] * 13 src-jtulach1.12 2/19/99 Petr Hamernik * 12 src-jtulach1.11 2/19/99 Jaroslav Tulach * 11 src-jtulach1.10 2/19/99 Jaroslav Tulach runAtomicAsUser * 10 src-jtulach1.9 2/19/99 Ales Novak * 9 src-jtulach1.8 2/15/99 Jesse Glick [JavaDoc] * 8 src-jtulach1.7 2/12/99 Jaroslav Tulach New interfaces for * printing and locking * 7 src-jtulach1.6 2/11/99 Jesse Glick Just notes: I think there * is code missing to init attribute sets. * 6 src-jtulach1.5 2/10/99 Jesse Glick [JavaDoc] * 5 src-jtulach1.4 2/10/99 Jesse Glick [JavaDoc] * 4 src-jtulach1.3 2/8/99 Jesse Glick [JavaDoc] * 3 src-jtulach1.2 1/29/99 Jaroslav Tulach * 2 src-jtulach1.1 1/28/99 Jaroslav Tulach * 1 src-jtulach1.0 1/28/99 Jaroslav Tulach * $ */